package logic

import (
	"context"
	"github.com/pkg/errors"
	"github.com/zeromicro/go-zero/core/logx"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"im_sys/apps/im/immodels"
	"im_sys/apps/im/rpc/im"
	"im_sys/apps/im/rpc/internal/svc"
	"im_sys/pkg/constants"
	"im_sys/pkg/wuid"
	"im_sys/pkg/xerr"
)

type SetUpUserConversationLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewSetUpUserConversationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SetUpUserConversationLogic {
	return &SetUpUserConversationLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

// 建立会话: 群聊, 私聊
func (l *SetUpUserConversationLogic) SetUpUserConversation(in *im.SetUpUserConversationReq) (*im.SetUpUserConversationResp, error) {
	// todo: add your logic here and delete this line
	switch constants.ChatType(in.ChatType) {
	case constants.SingleChatType:
		// 生成会话id
		cid := wuid.CombineId(in.RecvId, in.RecvId)
		// 验证是否建立过会话
		conv, err := l.svcCtx.ConversationModel.FindOneByCid(l.ctx, cid)
		if err != nil {
			if errors.Is(err, immodels.ErrNotFound) {
				// 如果会话不存在,创建新会话
				err = l.svcCtx.ConversationModel.Insert(l.ctx, &immodels.Conversation{
					ConversationId: cid,
					ChatType:       constants.ChatType(in.ChatType),
				})
				if err != nil {
					return nil, errors.Wrapf(xerr.NewDBErr(), "insert conversation failed: %v", err)
				}
			} else {
				return nil, errors.Wrapf(xerr.NewDBErr(), "find conversation by id failed: %v", err)
			}
		} else if conv != nil {
			return nil, nil // 已建立过会话
		}

		// 为发送方建立会话关联，会话立即显示在UI中
		err = l.relateUserConv(cid, in.SendId, in.RecvId, constants.ChatType(in.ChatType), true)
		if err != nil {
			return nil, err
		}
		// 为接收方建立会话关联，等收到消息后显示
		err = l.relateUserConv(cid, in.RecvId, in.SendId, constants.ChatType(in.ChatType), false)
		if err != nil {
			return nil, err
		}
	}
	return &im.SetUpUserConversationResp{}, nil
}

// 用于将会话与特定用户关联
func (l *SetUpUserConversationLogic) relateUserConv(cid, sendId, recvId string, chatType constants.ChatType, isShow bool) error {
	userConv, err := l.svcCtx.UserConversationModel.FindByUserId(l.ctx, sendId) // 查询用户的会话集合
	if err != nil {
		// 如果用户没有会话集合记录，则创建新的空会话集合（需要添加新会话到集合中，涉及更复杂的操作，适合统一处理，不直接插入）
		if errors.Is(err, immodels.ErrNotFound) {
			userConv = &immodels.UserConversation{
				ID:               primitive.NewObjectID(),
				UserId:           sendId,
				ConversationList: make(map[string]*immodels.Conversation),
			}
		} else {
			return errors.Wrapf(xerr.NewDBErr(), "find user conversation by user id failed: %v", err)
		}
	}

	// 检查该会话是否已经在用户的会话列表中
	if _, ok := userConv.ConversationList[cid]; ok {
		return nil
	}

	// 会话不在用户会话列表中，添加会话
	userConv.ConversationList[cid] = &immodels.Conversation{
		ConversationId: cid,
		ChatType:       chatType,
		IsShow:         isShow,
	}

	// 更新/插入用户会话列表
	// $set 操作符，在 data 中提供的字段都会被设置到匹配的文档中，而未在 data 中提供的字段将保持不变
	// upsert 选项的作用是：如果找不到符合条件的文档，则根据提供的数据创建一个新的文档
	err = l.svcCtx.UserConversationModel.InsertOrUpdate(l.ctx, userConv)
	if err != nil {
		return errors.Wrapf(xerr.NewDBErr(), "update/insert user conversation failed: %v", err)
	}
	return nil
}
